package OliveStory;

# Copyright (c) 2005, Shawn Boyette
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# * Redistributions of source code must retain the above copyright
#   notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright
#   notice, this list of conditions and the following disclaimer in
#   the documentation and/or other materials provided with the
#   distribution.
#  * Neither the name of the Olive Newsreader  nor the names of its
#    contributors may be used to endorse or promote products derived
#    from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.

require Exporter;
use warnings;
use strict;
use Date::Calc qw(Day_of_Week Mktime Timezone Delta_DHMS);
use Encode;
use OliveWindow;

our @ISA       = qw(Exporter);
our @EXPORT    = qw( &markall &markone &refreshlist &storyprev &storynext &shiftlist );


#######################################################################
##  story display logic  ##############################################
#######################################################################

sub markall {
    # sets the 'read' status of all db entries to true or false
    # depending on its second argument
    my ($cui,$ru) = @_;

    my $d = $cui->userdata->{dbh};
    my $statement = "UPDATE stories SET read = $ru WHERE id > 0";
    my $sth = $d->prepare($statement);
    $sth->execute;

    refreshlist($cui);
}

sub markone {
    # sets the 'read' status of current selected story to true or false
    # depending on its second argument
    my ($cui,$ru) = @_;
    my $w = $cui->userdata->{wins};

    my ($sel) = $w->{news}{list}->get_active_id;
    $cui->userdata->{nlsel} = $sel;
    $cui->userdata->{nlsel} = $sel + 1 if $ru; # bump ahead one if marking 'read'
    $sel = $w->{news}{list}{-values}[$sel];

    my $d = $cui->userdata->{dbh};
    my $statement = "UPDATE stories SET read = $ru WHERE id = $sel";
    my $sth = $d->prepare($statement);
    $sth->execute;

    refreshlist($cui);
    shiftlist($cui);
    $cui->userdata->{nlsel} = 0;
    $w->{news}{list}->set_selection(-10061);
}

#-------------------------------------------------------------

sub refreshlist {
    # (re)generates the olive story list
    my $cui = shift;
    my $w = $cui->userdata->{wins};
    my $c = $cui->userdata->{c};
    my $d = $cui->userdata->{dbh};
    my $x = $w->{dim}[0];
    my @values = ();
    my %labels = ();
    my $select = -1;
    my $fill   = $x - 26;
    my $i      = 0;
    my $j      = 0;
    my $r1     = 1;

    my @t = gmtime(( time() - ($cui->userdata->{tz} * 3600)));
    $cui->userdata->{ur}  = $d->selectrow_array("SELECT count(id) FROM stories WHERE read = 0");
    $cui->userdata->{new} = $d->selectrow_array("SELECT count(id) FROM stories WHERE new = 1");

    # set up statements for gathering stories
    my @statement = ();
    $statement[0] = "SELECT id,title,nick,timestamp,read FROM stories ".
        "WHERE new = 1 OR read = 0 ORDER BY timestamp";
    $statement[1] = "SELECT id,title,nick,timestamp,read FROM stories ".
        "WHERE new = 0 AND read = 1 ORDER BY timestamp DESC";

    # fetch, and build list
    for (0..(@statement - 1)) {
        my $sth = $d->prepare($statement[$_]);
        $sth->execute;
        while (my $q = $sth->fetchrow_arrayref) {            
            if ($_ == 1 && $i && $j && $r1) {
                $values[$i] = -10061; # NEWTON OS IS BETTER THAN YOU
                $labels{-10061} = '-' x $x;
                $i++;
                $r1 = 0;
            }
            my @t2 = gmtime($q->[3]);
            my $timestring = '';
            if ($q->[3] == 0) { # no time given in feed
                $timestring = "    ???";
            } else {
                my ($Dd,$Dh,$Dm,$Ds) = Delta_DHMS(($t2[5]+1900),($t2[4]+1),$t2[3], $t2[2],$t2[1],$t2[0],
                                                  ($t[5]+1900), ($t[4]+1), $t[3],  $t[2], $t[1], $t[0]);
                $timestring = sprintf("%02dh %02dm",$Dh,$Dm);
                $timestring = sprintf("%02dd %02dh",$Dd,$Dh) if $Dd;
                $timestring = "   +99d" if ($Dd > 99);
            }
            
            my $nick = sprintf("%-16s ",$c->{feeds}{$q->[2]}{disp});
            
            $q->[1] = "[Null]" unless (defined $q->[1]);
            my $label = $q->[1];
            $label = substr($label,0,$fill - 1);
            Encode::_utf8_on($label) if ($ENV{LC_ALL} and ($ENV{LC_ALL} =~ /UTF-8/));
            $label = $label . (' ' x ($fill - length($label)));
            
            my $rf = $q->[4] ? ' -' : ' @'; # read/unread indicator
            
            $values[$i] = $q->[0];
            $labels{$q->[0]} = $label . $nick . $timestring . $rf;
            $i++;
            $j = 1 if ($_ == 0 && $i);
        }
    }

    $select = $cui->userdata->{nlsel} if $cui->userdata->{nlsel};

    $w->{news}->delete('newslist') if (defined $w->{news}{list});
    $w->{news}{list} = $w->{news}->add('newslist', 'Listbox',
                                       -y      => 1,
                                       -values => \@values,
                                       -labels => \%labels,
                                       -height     => int($w->{dim}[1] / 2) - 2,
                                       -selected   => $select,
                                       -vscrollbar => 1,
                                       -onchange   => sub { storypick($cui) },
                                       -onselchange=> sub { winstatus($cui) 
                                                                if ($c->{sls} && !$cui->userdata->{shift})},
                                      );
    winflip($cui,'story') unless ($cui->userdata->{focus} eq 'story');
    winstatus($cui) if $c->{sls};
}

#-------------------------------------------------------------

sub shiftlist {
    my $cui = shift;
    my $w = $cui->userdata->{wins};

    $cui->userdata->{nlsel} = 0;
    $cui->userdata->{shift} = 1;

    my $halfheight = int($w->{news}{list}->canvasheight /2);
    my $i = my $j = 0;
    for $j (1..$halfheight - 1) {
        last if ($w->{news}{list}->{-ypos} == $w->{news}{list}->{-max_selected});
        $w->{news}{list}->option_next;
        $i++;
    }
    for (1..$i) {
        $w->{news}{list}->option_prev;
    }
    $i = 0;
    for $j (1..$halfheight) {
        last if ($w->{news}{list}->{-ypos} == 0);
        $w->{news}{list}->option_prev;
        $i++;
    }
    for (1..$i) {
        $w->{news}{list}->option_next;
    }
    $w->{news}{list}->draw;

    $cui->userdata->{shift} = 0;
    winstatus($cui) if $cui->userdata->{c}->{sls}
}

#-------------------------------------------------------------

sub storynext {
    my $cui = shift;
    my $w = $cui->userdata->{wins};
    my $c = $cui->userdata->{c};
    
    $cui->userdata->{shift} = 1;
    $w->{news}{list}->option_next;
    my $id = $w->{news}{list}->get_active_id;

    # handle skip-read
    if ($c->{snu}) {
        my $d = $cui->userdata->{dbh};
        my $rid = $w->{news}{list}{-values}[$id];
        my ($read) = $d->selectrow_array("SELECT read FROM stories WHERE id = $rid");
        my $lastid = -1;
        while ($read) {
            $w->{news}{list}->option_next;
            $rid = $w->{news}{list}{-values}[$w->{news}{list}->get_active_id];
            ($read) = $d->selectrow_array("SELECT read FROM stories WHERE id = $rid");
            last if ($lastid == $rid); # infinite loop escape
            $lastid = $rid;
        }
        $id = $w->{news}{list}->get_active_id;
    }

    # make sure we're not standing on the divider
    storynext($cui) if ( $w->{news}{list}{-values}[$id] == -10061);

    # set new selection
    $cui->userdata->{shift} = 0;
    $w->{news}{list}->set_selection($id);
}

#-------------------------------------------------------------

sub storyprev {
    my $cui = shift;
    my $w = $cui->userdata->{wins};
    my $c = $cui->userdata->{c};

    $cui->userdata->{shift} = 1;
    $w->{news}{list}->option_prev;
    my $id = $w->{news}{list}->get_active_id;

    # handle skip-read
    if ($c->{snu}) {
        my $d = $cui->userdata->{dbh};
        my $rid = $w->{news}{list}{-values}[$id];
        my ($read) = $d->selectrow_array("SELECT read FROM stories WHERE id = $rid");
        my $lastid = -1;
        while ($read) {
            $w->{news}{list}->option_prev;
            $rid = $w->{news}{list}{-values}[$w->{news}{list}->get_active_id];
            ($read) = $d->selectrow_array("SELECT read FROM stories WHERE id = $rid");
            last if ($lastid == $rid); # infinite loop escape
            $lastid = $rid;
        }
        $id = $w->{news}{list}->get_active_id;
    }

    # make sure we're not standing on the divider
    storyprev($cui) if ( $w->{news}{list}{-values}[$id] == -10061);

    # set new selection
    $cui->userdata->{shift} = 0;
    $w->{news}{list}->set_selection($id);
}

#-------------------------------------------------------------

sub storypick {
    my $cui = shift;
    my $w = $cui->userdata->{wins};
    my $c = $cui->userdata->{c};
    my $d = $cui->userdata->{dbh};
    
    my $sel = $w->{news}{list}->get;
    my $id  = $w->{news}{list}->get_active_id;
    return if (!defined $sel or $sel == -10061 or $sel eq ''); # hitting enter on the separator/with no feeds 
    

    $w->{news}{ftr1}->text('Fetching...' . ' ' x ($w->{dim}[0] - 11));
    $w->{news}{ftr1}->draw;

    my @story = $d->selectrow_array("SELECT nick,title,timestamp,desc,link,read FROM stories WHERE id = $sel");

    unless ($story[5]) { # if unread...
        my $statement = "UPDATE stories SET read = 1 WHERE id = $sel";
        my $sth = $d->prepare($statement);
        $sth->execute;
        # sekrit message passing for menu rebuild
        $cui->userdata->{nlsel} = $id;
        refreshlist($cui);
    }

    $cui->userdata->{chan} = $c->{feeds}{$story[0]}{title};

    $w->{news}{ftr1}->text(' ' x $w->{dim}[0]);
    winstatus($cui) if $c->{sls};
    $w->{news}{ftr1}->draw;

    # just in case (especially titles)
    $story[1] = "[NULL]" unless (defined $story[1]);
    $story[3] = "[NULL]" unless (defined $story[3]);

    # paint story
    Encode::_utf8_on($story[1]) if ($ENV{LC_ALL} and ($ENV{LC_ALL} =~ /UTF-8/));
    my $pad   = ' ' x int(($w->{dim}[0] - length($story[1])) / 2); 
    my $pad2  = ' ' x ( int(($w->{dim}[0] - length($story[1])) / 2) - 2 ); 
    my $title = join('',$pad,$story[1],$pad2,"\n",$pad,('-' x length($story[1])),$pad2,"\n\n");
    $story[3] = $title . $story[3] . "\n\n--\n" . $story[4];
    $w->{story}{view}->text($story[3]);
    $w->{story}{view}->cursor_to_home;
    $w->{story}{view}->draw;

    # there's been user action; reset the "polled since" hash
    undef $cui->userdata->{polled};
    undef $cui->userdata->{pwhen};

    # window+system housekeeping
    winflip($cui,'news') if ($c->{fos});
    shiftlist($cui);
}


1;
